package language.advanced.Concurrent;

import org.junit.Test;

import java.util.concurrent.*;
import java.util.stream.IntStream;

//使用线程池收集多线程返回结果的例子
public class CompleteFutureTest {
    @Test
    public void future(){
        //Future机制并不是对Runnable的革名，只是对Runnable的扩展。
        // Callable、Future、FutureTask的配合，解决Runnable无返回值的问题。
        ExecutorService executor = Executors.newCachedThreadPool();
        Task task = new Task();
        //Future<Integer> result = executor.submit(task);
        FutureTask<Integer> result = new FutureTask<>(task);
        executor.submit(result);
        executor.shutdown();

        System.out.println("主线程执行: "+ Thread.currentThread().getName());

        try {
            System.out.println("task运行结果"+result.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

        System.out.println("所有任务执行完毕");
    }

    static class Task implements Callable<Integer>{
        @Override
        public Integer call() throws Exception {
            System.out.println("子线程在进行计算: "+ Thread.currentThread().getName());
            Thread.sleep(3000);
            int sum = 0;
            for(int i=0;i<100;i++)
                sum += i;
            return sum;
        }
    }

    @Test
    public void singleCompleteableFuture() throws InterruptedException {
        System.out.println("主线程执行: " + Thread.currentThread().getName());

        ExecutorService executorService=Executors.newFixedThreadPool(3);
        //supplyAsync()可以定义返回值，现在使用的runAsync()由于传入的是Rnnable接口，是没有返回值的
        CompletableFuture<Integer> result = CompletableFuture
                .supplyAsync(Task2::call, executorService)
                .whenComplete((r, e) -> { System.out.println("执行结果为"+ r + " " + Thread.currentThread().getName()); })
                .exceptionally(e -> {
                    e.printStackTrace();
                    System.out.println("出现了异常结果");
                    return null;
                });
        // join
        // thenCompose A完成后(开另一个线程)做B  类似thenApplyAsync
        // thenCombine A、B同时做，做完做C
        // applyToEither
        if (result.isDone()){
            System.out.println("主线程执行完毕: " + Thread.currentThread().getName());
        }
    }

    @Test
    public void multiCompleteableFuture() throws InterruptedException, ExecutionException {
        System.out.println("主线程执行: " + Thread.currentThread().getName());

        ExecutorService executorService=Executors.newFixedThreadPool(3);
        ExecutorService writeExcelThread = Executors.newFixedThreadPool(1);

        //supplyAsync()可以定义返回值，现在使用的runAsync()由于传入的是Rnnable接口，是没有返回值的
        CompletableFuture[] tasks = IntStream.rangeClosed(0, 10)
                .mapToObj(pid -> CompletableFuture
                        .supplyAsync(()->call2(pid), executorService)
                        .thenAcceptAsync((r) -> { System.out.println("执行结果为"+ r + " " + Thread.currentThread().getName()); }, writeExcelThread)
                        .exceptionally(e -> {
                            e.printStackTrace();
                            System.out.println("出现了异常结果");
                            return null;
                        }))
                .toArray(CompletableFuture[]::new);

        for (CompletableFuture task : tasks) {
            task.join();
        }

        System.out.println("主线程执行完毕: " + Thread.currentThread().getName());
    }

    private Integer call2(int a){
        int num = (int)(Math.random()*100);
        System.out.println("子线程" + a + "在进行计算: "+ Thread.currentThread().getName());
        try {
            Thread.sleep(num*10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return num;
    };

    static class Task2 {
        public static Integer call(){
            System.out.println("子线程在进行计算: "+ Thread.currentThread().getName());
            int sum = 0;
            for(int i=0;i<100;i++)
                sum += i;
            return sum;
        }
    }

}
